package com.hup.joystick.view.quickMenu;

import com.hup.joystick.constant.DeviceKey.StandardKeys;
import com.hup.joystick.model.QuickMenuConfig;
import com.hup.joystick.view.customView.BaseMenuWindow;
import com.hup.joystick.view.quickMenu.QMAppItem.QMAppItemHandler;
import com.hup.joystick.view.quickMenu.QMFunctionButton.QMFunctionButtonHandler;
import com.hup.utils.commons.hardware.HRobotJni;
import com.hup.utils.commons.hardware.MouseUtil;
import com.hup.utils.commons.start.LookThemeUtil;
import com.hup.utils.commons.start.SpringContext;
import com.hup.utils.swing.ComponentUtil;
import com.hup.utils.swing.frame.SingletonWindowHelper;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Lazy;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

/**
 * 快捷菜单JWindow,展示时,不会影响以全屏的程序
 *
 * @author hugan
 * @date 2020/11/9
 */
@Log4j2
@Lazy
@org.springframework.stereotype.Component
public class QuickMenu extends BaseMenuWindow {
    public static final String TITLE = "快捷菜单";

    private final ViewItemHandler viewItemHandler = new ViewItemHandler();
    private final boolean isPreview;
    private QuickMenuConfig config;
    private QuickMenuForm form;

    /**
     * @param previewOwner null-通过手柄打开的窗口,唯一实例由Springboot管理;
     * notNull-配置页面的打开的窗口,唯一实例由SingletonWindowHelper管理,需要传入owner避免阻塞型Dialog阻挡本窗口
     */
    public static void showFrame(Window previewOwner, QuickMenuConfig previewConfig) {
        BaseMenuWindow.disposeShowingWindows();
        QuickMenu quickMenu;
        if (previewOwner == null) {
            quickMenu = SpringContext.getBean(QuickMenu.class);
        } else {//预览
            log.info("新建预览QuickMenu");
            quickMenu = new QuickMenu(previewOwner);
            SingletonWindowHelper.addWindow(quickMenu);
            quickMenu.config = previewConfig;
            quickMenu.viewItemHandler.initViewByConfig();
        }
        quickMenu.showMenuWindow();
    }

    public QuickMenu() {
        this(null);
    }

    private QuickMenu(Window previewOwner) {
        super(previewOwner, TITLE);
        config = new QuickMenuConfig();

        initView();
        initListener();
        isPreview = previewOwner != null;
        if (!isPreview) {
            //非预览时,创建对象后马上读取配置
            notifyConfigUpdated();
        }
        form.labJoyTips.setVisible(!isPreview);
    }

    private void initView() {
        form = new QuickMenuForm();
        setContentPane(form.root);

        form.labNoneItem.setFont(LookThemeUtil.getDefaultScaledFont().deriveFont(Font.BOLD, 28f));
        form.pFunction.removeAll();
        form.pFunction.setLayout(new GridLayout());
        form.pApp.removeAll();
        form.pApp.setLayout(new GridLayout());

        initTransparentBg(form.pFunction, form.pApp);

        setAlwaysOnTop(true);
    }

    private void initListener() {
        //点击背景退出
        form.root.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                QuickMenu.this.dispose();
            }
        });
        //键盘按键,仅预览模式下生效(有焦点)
        form.root.registerKeyboardAction(e -> dispose(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        //方向键
        form.root.registerKeyboardAction(e -> onPressJoyArrow(StandardKeys.ARROW_N.key), KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        form.root.registerKeyboardAction(e -> onPressJoyArrow(StandardKeys.ARROW_S.key), KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        form.root.registerKeyboardAction(e -> onPressJoyArrow(StandardKeys.ARROW_W.key), KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        form.root.registerKeyboardAction(e -> onPressJoyArrow(StandardKeys.ARROW_E.key), KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    }

    @Override
    public boolean isSingleton() {
        return false;//阻止SpringBoot创建本类时,添加对象到SingletonWindowHelper,
    }

    @Override
    public void dispose() {
        super.dispose();
        if (isPreview) SingletonWindowHelper.removeWindow(this);//预览时为了保持唯一
    }

    /**
     * 获取[(手柄)方向键]控制鼠标移动的目标坐标
     *
     * @param key {@link StandardKeys#ARROW_N}等
     * @return 鼠标坐标;null-无配置任何功能时返回null
     */
    private Point onPressArrow(String key) {
        int functionCount = form.pFunction.getComponentCount();
        int appCount = form.pApp.getComponentCount();
        int totalCount = functionCount + appCount;
        if (totalCount == 0) return null;

        //鼠标所在的控件
        Point mouseLoc = MouseUtil.getLocation();
        mouseLoc.x -= this.getX();
        mouseLoc.y -= this.getY();
        Component current = SwingUtilities.getDeepestComponentAt(form.root, mouseLoc.x, mouseLoc.y);
        log.info("mouseLoc={}, current={}", mouseLoc, current);
        Component target;//要鼠标移动到的控件
        if (current instanceof QMFunctionButton || current instanceof QMAppItem) {
            target = getMoveTarget(key, current);
        } else {
            //int targetIndex = totalCount == 1 ? 0 : totalCount / 2 - 1;//鼠标不在[功能item]上,或不在窗口上:选中最中间的控件
            int targetIndex = 0;//在第一个控件
            target = targetIndex < functionCount ? form.pFunction.getComponent(targetIndex) : form.pApp.getComponent(targetIndex - functionCount);
        }

        if (isPreview) {
            //预览时才有焦点
            target.requestFocus();
        }
        //目标位置:在控件中心偏下
        Point res = new Point();
        res.x = target.getWidth() / 2;
        res.y = (int) (target.getHeight() * 0.75);
        res = SwingUtilities.convertPoint(target, res, form.root);
        res.x += this.getX();
        res.y += this.getY();
        log.info("res={}, this=[{},{}]", res, this.getX(), this.getY());
        return res;
    }

    private Component getMoveTarget(String key, Component current) {
        boolean insideFunction = current instanceof QMFunctionButton;
        Container parentPanel = insideFunction ? form.pFunction : form.pApp;
        Point parentBlock = insideFunction ? viewItemHandler.functionBlock : viewItemHandler.appBlock;
        Container nextPanel = !insideFunction ? form.pFunction : form.pApp;
        Point nextBlock = !insideFunction ? viewItemHandler.functionBlock : viewItemHandler.appBlock;
        if (nextPanel.getComponentCount() == 0) {//只有一个panel
            nextPanel = parentPanel;
            nextBlock = parentBlock;
        }

        int functionCount = form.pFunction.getComponentCount();
        int appCount = form.pApp.getComponentCount();
        int totalCount = functionCount + appCount;

        //移动距离:忽略四角方向,否则很容易重复移动,手柄物理按键问题
        int xDiff = 0, yDiff = 0;
        if (StandardKeys.ARROW_W.key.equals(key)) {
            xDiff = -1;
        } else if (StandardKeys.ARROW_E.key.equals(key)) {
            xDiff = 1;
        } else if (StandardKeys.ARROW_N.key.equals(key)) {
            yDiff = -1;
        } else if (StandardKeys.ARROW_S.key.equals(key)) {
            yDiff = 1;
        }

        //控件在[两个panel集合]中的index
        int curIndex = insideFunction ? parentPanel.getComponentZOrder(current) : functionCount + parentPanel.getComponentZOrder(current);
        int targetIndex = curIndex;
        //int targetIndex = insideFunction ? parentPanel.getComponentZOrder(current) : functionCount + parentPanel.getComponentZOrder(current);
        int parentItemCount = parentPanel.getComponentCount();

        if (xDiff != 0) {//左右移动
            targetIndex += xDiff;
        } else if (yDiff != 0) {//上下移动
            /*
            正常情况:如果每行都是铺满的,则直接±列数就可以了,但不铺满时要特殊处理
            布局:
                123     1234    123
                1       123     1
                1234    1       12345
                12              1234
            不能连续移动两行
            .当前行index,
            .目标行最大index
            .下移:
                倒数2->倒数1;倒数1->下一个panel;
                最大index是目标行的最后一个
             */
            int curXInParent = parentPanel.getComponentZOrder(current);
            boolean isFirstRow = curXInParent < parentBlock.x;
            boolean isLastRow = curXInParent >= parentBlock.x * (parentBlock.y - 1);
            boolean isLast2Row = false;//倒数第二行
            if (!isLastRow && parentBlock.y > 1) isLast2Row = curXInParent >= parentBlock.x * (parentBlock.y - 2);
            int curXIndex = parentPanel.getComponentZOrder(current) % parentBlock.x;//控件在当前行中的xIndex
            log.debug("yDiff={}, sFirstRow={}, isLastRow={}, isLast2Row={}, curXIndex={}, curIndex={}", yDiff, isFirstRow, isLastRow, isLast2Row, curXIndex, curIndex);

            if (yDiff < 0 && isFirstRow) {
                //在第一行上移
                int upMaxCount = nextPanel.getComponentCount() % nextBlock.x;//上个panel的最后一行数量
                int upMaxXIndex = upMaxCount == 0 ? nextBlock.x - 1 : upMaxCount - 1;//上一行的最大index
                int targetXIndex = Math.min(curXIndex, upMaxXIndex);
                int diff = nextBlock.x - targetXIndex;//目标行XIndex,其后面的数量(包括其本身)
                targetIndex = insideFunction ? parentItemCount + (nextBlock.x * (nextBlock.y - 1)) + targetXIndex : (nextBlock.x * (nextBlock.y - 1)) + targetXIndex;
                log.debug("upMaxXIndex={}, diff={}", upMaxXIndex, diff);
            } else if (yDiff > 0 && isLast2Row) {
                //在倒数第二行下移
                int downMaxCount = parentItemCount % parentBlock.x;
                int downMaxXIndex = downMaxCount == 0 ? parentBlock.x - 1 : downMaxCount - 1;//最后一行最大Xindex
                int diff = parentBlock.x - curXIndex;//本行后面的数量(包括当前item)
                targetIndex = curIndex + diff + Math.min(curXIndex, downMaxXIndex);
                log.debug("diff={}, downMaxXIndex={}", diff, downMaxXIndex);
            } else if (yDiff > 0 && isLastRow) {
                //在最后一行下移
                int downMaxXIndex = nextBlock.y > 1 ? nextBlock.x - 1 : nextPanel.getComponentCount() - 1;//下一行的最大index
                int targetXIndex = Math.min(curXIndex, downMaxXIndex);
                targetIndex = insideFunction ? functionCount + targetXIndex : targetXIndex;
            } else {
                //其他行上下移:不会越出当前panel
                targetIndex += yDiff * parentBlock.x;
            }
        }

        if (targetIndex < 0) {
            targetIndex = totalCount + targetIndex;
        } else if (targetIndex >= totalCount) {
            targetIndex = targetIndex - totalCount;
        }
        return targetIndex < functionCount ? form.pFunction.getComponent(targetIndex)
                : form.pApp.getComponent(targetIndex - functionCount);
    }

    /**
     * 设置界面保存配置后,通知SpringBoot的实例更新数据
     */
    public void notifyConfigUpdated() {
        config.loadFromFile();
        viewItemHandler.initViewByConfig();
    }

    /**
     * 实现控制鼠标移动到指定功能
     *
     * @param key {@link StandardKeys#ARROW_N}等
     */
    public void onPressJoyArrow(String key) {
        Point toMove = onPressArrow(key);
        if (toMove != null) HRobotJni.mouseMove(toMove);
    }

    private class ViewItemHandler {

        /**
         * [x=列数, y=行数]
         */
        private final Point functionBlock = new Point();
        private final Point appBlock = new Point();

        private final QMFunctionButtonHandler functionItemHandler = new QMFunctionButton.QMFunctionButtonHandler();
        private final QMAppItemHandler appItemHandler = new QMAppItemHandler();

        public void initViewByConfig() {
            log.debug("");
            updatePanel(form.pFunction, config.lstFunction, functionBlock, functionItemHandler);
            updatePanel(form.pApp, config.lstApp, appBlock, appItemHandler);

            form.labNoneItem.setVisible(config.lstFunction.isEmpty() && config.lstApp.isEmpty());
            ComponentUtil.afterChangedChild(form.root);
        }

        @SuppressWarnings("unchecked")
        private <I extends java.awt.Component, D> void updatePanel(JPanel panel, ArrayList<D> list, Point layoutSize,
                ItemHandler<I, D> itemHandler) {
            GridLayout layout = (GridLayout) panel.getLayout();
            int size = list.size();
            if (size == 0) {
                panel.setVisible(false);
                panel.removeAll();
                layoutSize.x = layoutSize.y = 0;
                layout.setColumns(layoutSize.x);
                layout.setRows(1);
                return;
            }
            //row*col的宫格,row<col
            int row = (int) Math.sqrt(size);
            int col = size / row;
            if (size % row != 0) col++;
            log.debug("{}=[row={},col={}]", size, row, col);
            layoutSize.x = col;
            layoutSize.y = row;
            layout.setColumns(layoutSize.x);
            layout.setRows(layoutSize.y);

            /*
            初始化列表:
            .先删除多余项目,再填充数据
             */
            for (int i = panel.getComponentCount() - 1; i >= size; i--) panel.remove(i);
            for (int index = 0; index < size; index++) {
                int count = panel.getComponentCount();
                log.debug("count={}, index={}", count, index);
                I item;
                if (index > count - 1) {
                    item = itemHandler.createItem();
                    panel.add(item);
                } else {
                    item = (I) panel.getComponent(index);
                }
                itemHandler.initData(item, list.get(index));
            }

            if (!panel.isVisible()) panel.setVisible(true);
        }

    }

    public interface ItemHandler<I extends java.awt.Component, D> {
        I createItem();

        void initData(I item, D data);
    }
}
